package com.ruoyi.service.fileshare.impl;

import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.enums.OperationType;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.Md5Util;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.domain.fileshare.dto.*;
import com.ruoyi.domain.fileshare.entity.*;
import com.ruoyi.domain.fileshare.vo.FileShareDetailVo;
import com.ruoyi.domain.fileshare.vo.FileShareVO;
import com.ruoyi.mapper.fileshare.FileShareMapper;
import com.ruoyi.mapper.fileshare.MailUserMapper;
import com.ruoyi.mapper.fileshare.ShareLogMapper;
import com.ruoyi.mapper.fileshare.SysFileMapper;
import com.ruoyi.service.fileshare.EmailService;
import com.ruoyi.service.fileshare.FileShareService;
import com.ruoyi.system.service.ISnowflakeService;
import com.ruoyi.system.service.ISysUserService;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import javax.mail.internet.MimeMessage;
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Service
public class FileShareServiceImpl implements FileShareService {

    @Resource
    private MailUserMapper mailUserMapper; // 邮件用户
    @Resource
    private SysFileMapper sysFileMapper; // 文件信息
    @Resource
    private ShareLogMapper shareLogMapper; // 分享日志（文件发送日志）
    @Resource
    private ISnowflakeService snowflakeService;
    @Resource
    private EmailService emailService; // 邮件服务
    @Value("${spring.mail.username}")
    private String senderAddress;
    @Autowired
    private RedisCache redisCache;
    @Resource
    private FileShareMapper fileShareMapper;
    @Resource
    private ISysUserService userService;

    private static final String ERROR_MESSAGE_PREFIX_ADD = "新增文件失败：";

    /**
     * 保存已上传的文件信息
     * */
    @Override
    @Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRED)
    public boolean addFileInfo(List<SysFileDto> dtoList) {
        if(CollectionUtils.isEmpty(dtoList)){
            throw new ServiceException(ERROR_MESSAGE_PREFIX_ADD+"要保存的文件不能为空。");
        }
        var batchLogs = new ArrayList<ShareLog>();
        var batchFiles = new ArrayList<SysFile>();
        var shareMap = new HashMap<Long,List<Long>>();
        for (SysFileDto dto : dtoList) {
            SysFile file = new SysFile();
            String path = dto.getPath();
            if(!StringUtils.hasLength(path)){
                throw new ServiceException(ERROR_MESSAGE_PREFIX_ADD);
            }
            //  判断文件是否重复
            try(InputStream inputStream = new FileInputStream(path)){
                String fileMd5 = Md5Util.md5Abstract(inputStream);
                file.setHash(fileMd5);
            }catch (Exception e){
                throw new ServiceException(ERROR_MESSAGE_PREFIX_ADD+"哈希值计算失败。");
            }
            file.setCreateUser(SecurityUtils.getLoginUser().getUserId().toString());
            file.setIsDeleted(false);
            var rpList = sysFileMapper.queryWithSelectiveFields(file);
            if(!CollectionUtils.isEmpty(rpList)){
                throw new ServiceException(ERROR_MESSAGE_PREFIX_ADD+" 文件："+dto.getOriginalName()+"已存在。");
            }
            BeanUtils.copyProperties(dto,file);
            var loginUser = SecurityUtils.getLoginUser();
            file.setCreateUser(loginUser.getUserId().toString());
            file.setUpdateUser(loginUser.getUserId().toString());
            Long fileId = snowflakeService.getSnowflakeId();
            file.setId(fileId);
//            if(!CollectionUtils.isEmpty(dto.getShareIds()))
//                file.setShareUserIds(dto.getShareIds().stream().map(String::valueOf).collect(Collectors.joining(";")));
            batchFiles.add(file);
            var log = new ShareLog(
                    file.getId(),
                    "新增文件："+dto.getOriginalName(),
                    file.getResourceId(),
                    OperationType.ADDITION.name()
            );
            log.setCreateUser(loginUser.getUserId().toString());
            log.setUpdateUser(loginUser.getUserId().toString());
            log.setId(UUID.randomUUID().toString());
            batchLogs.add(log);
            if(!CollectionUtils.isEmpty(dto.getShareIds())&&!shareMap.containsKey(fileId)){
                shareMap.put(fileId,dto.getShareIds().stream().map(Long::valueOf).collect(Collectors.toList()));
            }
        }
        sysFileMapper.insertBatch(batchFiles);
        shareLogMapper.insertBatch(batchLogs);
        for (Map.Entry<Long, List<Long>> entry : shareMap.entrySet()) {
            addFileShare(List.of(entry.getKey()),entry.getValue());
        }
        return true;
    }
    @Override
    public boolean updateFileInfo(SysFileDto dto) {
        return true;
    }

    /**
     * 删除已上传的文件信息
     * */
    @Override
    @Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRED)
    public boolean deleteFileInfo(String ids) {
        if(!StringUtils.hasLength(ids)){
            throw new ServiceException("删除文件失败：要删除的文件id不能为空。");
        }
        var batchLogs = new ArrayList<ShareLog>();
        var batchFiles = new ArrayList<Long>();
        var loginUser = SecurityUtils.getLoginUser();
        String[] idArr = ids.split(",");
        for (String id : idArr) {
            if (!StringUtils.hasLength(id)) {
                continue;
            }
            var log = new ShareLog(
                    Long.parseLong(id),
                    "删除文件",
                    null,
                    OperationType.DELETE.name()
            );
            var share = new FileShare();
            share.setFileId(Long.parseLong(id));
            var shares = fileShareMapper.selectWithSelectiveFields(share);
            // 删除共享关系
            if(!CollectionUtils.isEmpty(shares)){
                for (FileShare fileShare : shares) {
                    fileShare.setIsDeleted(true);
                    fileShareMapper.updateByPrimaryKeySelective(fileShare);
                }
            }
            batchFiles.add(Long.valueOf(id));
            log.setCreateUser(loginUser.getUserId().toString());
            log.setUpdateUser(loginUser.getUserId().toString());
            log.setId(UUID.randomUUID().toString());
            batchLogs.add(log);
        }
        sysFileMapper.deleteBatch(batchFiles);
        shareLogMapper.insertBatch(batchLogs);
        return true;
    }

    /**
     * 获取用户上传文件列表
     * */
    @Override
    public  PageInfo<SysFile> listFiles(SysFileQueryDto dto) {
        var file = new SysFile();
        BeanUtils.copyProperties(dto,file);
        file.setIsDeleted(false);
        file.setCreateUser(SecurityUtils.getLoginUser().getUserId().toString());
        PageHelper.startPage(dto.getPage(), dto.getLimit());
        var list = sysFileMapper.queryWithSelectiveFields(file);
        return new PageInfo<>(list);
    }

    @Override
    public List<MailUser> listMailUser() {
        return mailUserMapper.selectAll();
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean sendFiles(MailUserSendDto dto) {
        if(CollectionUtils.isEmpty(dto.getReceiverIds())){
            throw new ServiceException("发送邮件失败：收件人不能为空。");
        }
        if(CollectionUtils.isEmpty(dto.getFileIds())){
            throw new ServiceException("发送邮件失败：要发送的文件不能为空。");
        }
        if(!StringUtils.hasLength(senderAddress)){
            throw new ServiceException("发送邮件失败：在发送邮件前请先在配置文件application.yml中正确配置发件人邮箱地址以及密码。");
        }
        var opName = OperationType.SHARE_EMAIL.name();
        var loginUserId = String.valueOf(SecurityUtils.getLoginUser().getUserId());
        var mailId =snowflakeService.getSnowflakeId();
        // 查询邮件附件信息
        var attachments = sysFileMapper.selectByIds(dto.getFileIds());
        var receiverList = mailUserMapper.selectReceiverByIds(dto.getReceiverIds());
        if(CollectionUtils.isEmpty(attachments)){
            throw new ServiceException("发送邮件失败：要发送的文件不存在。");
        }
        if(CollectionUtils.isEmpty(receiverList)){
            throw new ServiceException("发送邮件失败：接收人不存在。");
        }
        MimeMessage mimeMessage;
        if(dto.getIsCompress()){
            mimeMessage = emailService.buildMimeMessageCompressAttachments(
                    mailId.toString(),
                    senderAddress,
                    receiverList.stream().map(MailUser::getEmail).collect(Collectors.toList()),
                    dto.getTitle(),
                    dto.getContent(),
                    attachments
            );
        }else{
            mimeMessage = emailService.buildMimeMessage(
                    mailId.toString(),
                    senderAddress,
                    receiverList.stream().map(MailUser::getEmail).collect(Collectors.toList()),
                    dto.getTitle(),
                    dto.getContent(),
                    attachments
            );
        }
        emailService.send(mimeMessage);
        // 记录日志: 生成日志记录项列表，再批量插入日志
        List<ShareLog> logList = new ArrayList<>();

        for(SysFile file : attachments){
            var updateFile = new SysFile();
            updateFile.setId(file.getId());
            var shareFiles = file.getShareUserIds();
            List<String> splits = new ArrayList<>();
            if(StringUtils.hasLength(shareFiles)){
                splits = new ArrayList<>(List.of(shareFiles.split(";")));
            }
            for(MailUser mailUser : receiverList){
                if (splits.stream().noneMatch(ss->ss.equals(mailUser.getSysUserId()))) {
                    splits.add(mailUser.getSysUserId());
                }
                var log = new ShareLog(file.getId(), "邮件分享文件："+file.getOriginalName()+"。", mailUser.getId(), opName);
                log.setId(UUID.randomUUID().toString());
                log.setCreateUser(loginUserId);
                log.setUpdateUser(loginUserId);
                logList.add(log);
            }
            updateFile.setShareUserIds(String.join(";",splits));
            sysFileMapper.updateByPrimaryKeySelective(updateFile);
        }
        shareLogMapper.insertBatch(logList);
        return true;
    }

    @Override
    public boolean addOrUpdateMailUser(MailUserAddDto dto){
        var loginUserId = SecurityUtils.getLoginUser().getUserId();
        var mailUser = new MailUser();
        if(dto.getId()==null){
            var id = snowflakeService.getSnowflakeId();
            // 判断绑定系统用户是否重复，一个系统用户只能绑定一个供应商
            mailUser.setSysUserId(dto.getSysUserId());
            mailUser.setIsDeleted(false);
            var rp = mailUserMapper.selectWithSelectiveFields(mailUser);
            if(!CollectionUtils.isEmpty(rp)){
                throw new ServiceException("新增失败：绑定系统用户重复。");
            }
            mailUser.setAccount(dto.getAccount());
            mailUser.setEmail(dto.getEmail());
            var rpList = mailUserMapper.selectWithSelectiveFields(mailUser);
            if(!CollectionUtils.isEmpty(rpList)){
                throw new ServiceException("新增失败：重复添加。");
            }
            BeanUtils.copyProperties(dto,mailUser);
            mailUser.setId(id);
            mailUser.setCreateUser(loginUserId.toString());
            mailUser.setUpdateUser(loginUserId.toString());
            mailUserMapper.insertSelective(mailUser);
        }else{
            var item = mailUserMapper.selectByPrimaryKey(dto.getId());
            if(null==item){
                throw new ServiceException("修改失败：用户不存在。");
            }
            // 判断此次绑定的系统用户是否与数据库中的一致
            if(StringUtils.hasLength(dto.getSysUserId())&&!dto.getSysUserId().equals(item.getSysUserId())){
                mailUser.setSysUserId(dto.getSysUserId());
                mailUser.setIsDeleted(false);
                var rp = mailUserMapper.selectWithSelectiveFields(mailUser);
                if(!CollectionUtils.isEmpty(rp)){
                    throw new ServiceException("新增失败：绑定系统用户重复。");
                }
            }
            BeanUtils.copyProperties(dto,mailUser);
            mailUser.setUpdateUser(loginUserId.toString());
            mailUser.setUpdateTime(new Date());
            mailUserMapper.updateByPrimaryKeySelective(mailUser);
        }
        return true;
    }

    @Override
    public List<FileShareDetailVo> getFileShareDetail(Long fileId) {
        return sysFileMapper.getFileShareDetail(fileId);
    }

    @Override
    public PageInfo<MailUser> listMailUser(MailUserQueryDto dto) {
        var mailUser = new MailUser();
        BeanUtils.copyProperties(dto,mailUser);
        PageHelper.startPage(dto.getPage(), dto.getLimit());
        var list = mailUserMapper.selectWithSelectiveFields(mailUser);
        return new PageInfo<>(list);
    }

    @Override
    public List<MailUser> listSelectMailUser() {
        return mailUserMapper.selectUsersWithMailUser();
    }

    @Override
    public boolean deleteMailUser(Long id) {
        var dbItem = mailUserMapper.selectByPrimaryKey(id);
        if(null==dbItem){
            throw new ServiceException("停用失败：供应商不存在。");
        }
        if(dbItem.getIsDeleted()){
            throw new ServiceException("停用失败：此供应商已停用请勿重复操作。");
        }
        var mailUser = new MailUser();
        mailUser.setId(dbItem.getId());
        mailUser.setIsDeleted(true);
        mailUserMapper.updateByPrimaryKeySelective(mailUser);
        return true;
    }

    @Override
    public SupplierMetaDto getUserSupplierMeta() {
        var userId = SecurityUtils.getLoginUser().getUserId();
        var cacheKey = "supplier-meta:"+userId.toString();
        if (redisCache.hasKey(cacheKey)) {
            return redisCache.getCacheObject(cacheKey);
        }
        SupplierMetaDto dto = new SupplierMetaDto();
        var mailUser = new MailUser();
        mailUser.setSysUserId(userId.toString());
        var supplierUserList = mailUserMapper.selectWithSelectiveFields(mailUser);
        if(CollectionUtils.isEmpty(supplierUserList)){
            dto.setIsSupplier(false);
            dto.setSystemUserId(userId);
            redisCache.setCacheObject(cacheKey, dto,5, TimeUnit.MINUTES);
            return dto;
        }
        var supplier = supplierUserList.get(supplierUserList.size() - 1);
        dto.setIsSupplier(!supplier.getIsDeleted());
        dto.setSupplierId(supplier.getId());
        dto.setSupplierName(supplier.getAccount());
        dto.setSystemUserId(userId);
        dto.setIsDeleted(supplier.getIsDeleted());
        redisCache.setCacheObject(cacheKey, dto,16, TimeUnit.MINUTES);
        return dto;
    }

    @Override
    public boolean downloadLog(SysFile sysFile) {
        var loginUserId = SecurityUtils.getLoginUser().getUserId();
        var log = new ShareLog(
                sysFile.getId(),
                "下载文件："+sysFile.getOriginalName(),
                sysFile.getResourceId(),
                OperationType.DOWNLOAD.name()
        );
        log.setId(UUID.randomUUID().toString());
        log.setCreateUser(loginUserId.toString());
        log.setUpdateUser(loginUserId.toString());
        log.setCreateTime(new Date());
        log.setUpdateTime(new Date());
        shareLogMapper.insertSelective(log);
        return false;
    }

//    @Transactional(rollbackFor = Exception.class)
    /**
     * 将fieldIds中指定的文件分享给userIds中指定的用户
     * 1. 检查文件是否存在
     * 2. 检查用户是否存在
     * 3. 检查文件是否已经分享给用户
     * 4. 插入文件分享记录
     * 5. 插入分享日志
     * */
    @Override
    public void addFileShare(List<Long> fileIds, List<Long> userIds) {
        var batchData = new ArrayList<FileShare>();
        var batchLogs = new ArrayList<ShareLog>();
        var loginUserId = SecurityUtils.getLoginUser().getUserId();
        var originFileNames = new ArrayList<String>();
        int fileIdSize = fileIds.size(),userCount = userIds.size();
        int i=0,j=0,k=0;
        for (Long fileId : fileIds) {
            i++;
            SysFile file;
            if(i<=fileIdSize){
                // 检查文件是否存在
                file = sysFileMapper.selectByPrimaryKey(fileId);
                if(null==file){
                    throw new ServiceException("分享失败：文件不存在。");
                }
                originFileNames.add(file.getOriginalName());
                var updateFile = new SysFile();
                updateFile.setId(fileId);
                if(StringUtils.hasLength(file.getShareUserIds())){
                    var splits = new ArrayList<>(List.of(file.getShareUserIds().split(";")));
                    for (Long userId : userIds) {
                        if(splits.stream().noneMatch(ss->ss.equals(userId.toString()))){
                            splits.add(userId.toString());
                        }
                    }
                    updateFile.setShareUserIds(String.join(";",splits));
                }else{
                    updateFile.setShareUserIds(userIds.stream().map(String::valueOf).collect(Collectors.joining(";")));
                }
                sysFileMapper.updateByPrimaryKeySelective(updateFile);
            }
            if(k+1>fileIdSize){
                k=0;
            }
            for (Long userId : userIds) {
                if(Objects.equals(userId, loginUserId)){
                    throw new ServiceException("分享失败：不能分享给自己。");
                }
                j++;
                SysUser sysUser;
                if(j<=userCount){
                    // 检查用户是否存在
                    sysUser = userService.selectUserById(userId);
                    if(null==sysUser){
                        throw new ServiceException("分享失败：用户不存在。");
                    }
                }
                var item = new FileShare();
                item.setFileId(fileId);
                item.setUserId(userId);
                item.setIsDeleted(false);
                // 判断此文件是否已分享给这个用户
                var ls = fileShareMapper.selectWithSelectiveFields(item);
                if(!CollectionUtils.isEmpty(ls)){
                    throw new ServiceException("分享失败：文件已分享给用户，不要重复分享哦。");
                }
                item.setCreateUser(loginUserId.toString());
                item.setUpdateUser(loginUserId.toString());
                batchData.add(item);
                var log = new ShareLog(fileId, "共享文件："+originFileNames.get(k)+"。", userId, OperationType.SYSTEM_SHARE.name());
                log.setId(UUID.randomUUID().toString());
                log.setCreateUser(loginUserId.toString());
                log.setUpdateUser(loginUserId.toString());
                log.setReceiver(userId);
                log.setIsDeleted(false);
                batchLogs.add(log);
            }
            k++;
        }
        fileShareMapper.batchInsert(batchData);
        shareLogMapper.insertBatch(batchLogs);
    }

    @Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRED)
    @Override
    public void cancelShare(FileShareSubmitDto dto) {
        dto.getUserIdList().forEach(userId->{
            var fileShare = new FileShare();
            fileShare.setFileId(dto.getFileId());
            fileShare.setUserId(userId);
            fileShare.setIsDeleted(false);
            var ls = fileShareMapper.selectWithSelectiveFields(fileShare);
            if(CollectionUtils.isEmpty(ls)){
                throw new ServiceException("取消分享失败：文件分享关系不存在。");
            }
            var entity = ls.get(0);
            entity.setIsDeleted(true);
            fileShareMapper.updateByPrimaryKeySelective(entity);
        });
        var dbFile = sysFileMapper.selectByPrimaryKey(dto.getFileId());
        if(null==dbFile){
            throw new ServiceException("取消分享失败：文件不存在。");
        }
        var splits = new ArrayList<>(List.of(dbFile.getShareUserIds().split(";")));
        splits.removeIf(s->dto.getUserIdList().contains(Long.valueOf(s)));
        var updateFile = new SysFile();
        updateFile.setId(dto.getFileId());
        updateFile.setShareUserIds(String.join(";",splits));
        sysFileMapper.updateByPrimaryKeySelective(updateFile);
    }

    @Override
    public List<FileShareVO> getShareableList(Long fileId, Boolean isAdd) {
        var fs = new FileShareQuery();
        fs.setFileId(fileId);
        fs.setIsAdd(isAdd);
        var ls = fileShareMapper.selectVOsWithSelectiveFields(fs);
        var loginUserId = SecurityUtils.getLoginUser().getUserId();
        ls.forEach(item->{
            if(Objects.equals(item.getUserId(), loginUserId)){
                item.setDisabled(true);
            }
        });
        return ls;
    }

    @Override
    public List<MailUser> getUserChargeSupplierList(){
        var loginUserId = SecurityUtils.getLoginUser().getUserId();
        var mailUser = new MailUser();
        mailUser.setChargeUser(loginUserId.toString());
        mailUser.setIsDeleted(false);
        return mailUserMapper.selectWithSelectiveFields(mailUser);
    }
}
